import sys
import json
import copy
import jsonpath
import jsonschema
import pymysql
from common.log import logger
from common.sql_operate import SqlOperate
from common.common_config import db_ip, db_name_case, db_username, db_password, db_port

sql = SqlOperate(db_ip, db_name_case, db_username, db_password, db_port)


class ResultCompare(object):
    """
    接口比较级别： 1-return_code(关键参数值)验证、2-结构完整性验证、3-参数完整性验证、4-特殊参数值验证
    """

    def __init__(self, interface_case: dict, response: dict, table_name: str):
        """
        :param interface_case: 从数据库interface_case表中获取一条测试用例数据
        :param response: 接口请求返回的响应，包含"status_code, text, cookies, duration，还未更新到数据库
        :param table_name: 待操作的数据库表名【最好能将"测试结果更新"操作抽离出来，该模块只用于结果对比】
        """
        try:
            self.interface_case = interface_case
            self.table_name = table_name
            self.response_content = response.get("text") if "text" in response.keys() else "{}"
            self.response_duration = response.get("duration") if "duration" in response.keys() else 65535
            self.params = set()
        except TypeError as e:
            logger.error(f"[{self.__class__.__name__}] Type Error.")
        except Exception as e:
            logger.error(f"[{self.__class__.__name__}] {e}")
            logger.error(f"[{self.__class__.__name__}] Class Instance Init Error")

    def compare_return_code(self) -> dict:
        """
        return_code验证，即关键参数验证
        :return: 以字典形式返回，code表示方法执行结果，message表示方法执行结果描述，data表示方法需返回的数据
        """
        try:
            """
                1. 通过SqlOperate.select_one()方法，查询一条测试用例数据
                2. 预判该测试用例数据的response_result字段值的数据类型，若是字符串且以“{”开头，则通过json.loads()方法转换为dict类型
                3. 再判response_result字段值是否是dict类型，若不是，则该测试用例的请求返回值不合法，若是，则进行步骤4
                4. 再从测试用例数据的expect_code字段值是否等于测试用例数据的response_result字段值中的实际数值，比较两者是否相等
            """
            response_content = json.loads(self.response_content, strict=False)
            if response_content and isinstance(response_content, dict):
                actual_return_code = str(response_content[self.interface_case["return_code_to_compare"]])
                expect_return_code = str(self.interface_case["expect_return_code"])
                if actual_return_code == expect_return_code:
                    result = {"code": "000", "message": "关键参数值验证通过",
                              "data": {"expect_result": expect_return_code, "actual_result": actual_return_code}}
                else:
                    result = {"code": "001", "message": "关键参数值验证不通过",
                              "data": {"expect_result": expect_return_code, "actual_result": actual_return_code}}
            else:
                result = {"code": "400", "message": "响应正文格式不合法", "data": {}}
        except Exception as e:
            logger.error(f"[{self.__class__.__name__}.{sys._getframe().f_code.co_name}] {e}")
            result = {"code": "500", "message": "关键参数值验证出错", "data": {}}
        return result

    def compare_param_structure(self):
        """
        结构完整性验证
        :return: 以字典形式返回，code表示方法执行结果，message表示方法执行结果描述，data表示方法需返回的数据
        """
        try:
            response_content = json.loads(self.response_content)
            if isinstance(response_content, dict):
                self.interface_case["json_schema"] = self.interface_case["json_schema"].replace('\n', '')
                json_schema = json.loads(self.interface_case["json_schema"])
                jsonschema.validate(response_content, json_schema, format_checker=jsonschema.draft7_format_checker)
            else:
                raise TypeError()
        except jsonschema.ValidationError as e:
            logger.error(f"[{self.__class__.__name__}] Json Validation Failure.")
            logger.error(f"[{self.__class__.__name__}] Failure Position: {'.'.join([str(i) for i in e.path])}")
            logger.error(f"[{self.__class__.__name__}] Failure Info: {e.message}")
            result = {"code": "001", "message": f"结构完整性验证不通过，位置：{'.'.join([str(i) for i in e.path])}，信息：{e.message}",
                      "data": []}
        except jsonschema.SchemaError as e:
            logger.error(f"[{self.__class__.__name__}] Json Schema Error.")
            logger.error(f"[{self.__class__.__name__}] Error Position: {'.'.join([i for i in e.path])}")
            logger.error(f"[{self.__class__.__name__}] Error Info: {e.message}")
            result = {"code": "400", "message": f"验证模式出错，位置：{'.'.join([i for i in e.path])}，信息：{e.message}", "data": []}
        except TypeError as e:
            logger.error(f"[{self.__class__.__name__}.{sys._getframe().f_code.co_name}] {e}")
            logger.error("typeError")
            result = {"code": "500", "message": "数据类型错误", "data": []}
        except Exception as e:
            logger.error(f"[{self.__class__.__name__}.{sys._getframe().f_code.co_name}] {e}")
            logger.error(
                f"[{self.__class__.__name__}.{sys._getframe().f_code.co_name}] interface_case['json_schema']: {self.interface_case['json_schema']}")
            result = {"code": "500", "message": "结构完整性验证出错", "data": []}
        else:
            result = {"code": "000", "message": "结构完整性验证通过", "data": []}
        return result

    def __recursion(self, dict_obj: dict):
        """
        递归处理字典数据（json数据），提取其中的所有键
        :param dict_obj: 待处理的字典数据
        :return: 无返回，提取的键均添加到集合中
        """
        if isinstance(dict_obj, str) and dict_obj.startswith("{"):
            dict_obj = json.loads(dict_obj)
            self.__recursion(dict_obj)
        elif isinstance(dict_obj, dict):
            for key, value in dict_obj.items():
                self.params.add(key)
                if isinstance(value, dict):
                    self.__recursion(value)
                elif isinstance(value, list):
                    for element in value:
                        self.__recursion(element)
                else:
                    continue
        else:
            pass

    def compare_params_complete(self):
        """
        参数完整性验证
        :return: 以字典形式返回，code表示方法执行结果，message表示方法执行结果描述，data表示方法需返回的数据
        """
        try:
            response_content = json.loads(self.response_content)
            if isinstance(response_content, dict):
                self.__recursion(response_content)
                params = pymysql.escape_string(str(self.params))
                expect_result = set(eval(self.interface_case["expect_params"]))
                actual_result = copy.deepcopy(self.params)  # self.params
                logger.debug(f"expect_result: {expect_result}")
                logger.debug(f"actual_result: {actual_result}")
                if expect_result <= actual_result:
                    result = {"code": "000", "message": "参数完整性验证通过",
                              "data": {"expect_result": expect_result, "actual_result": actual_result, "params": params}}
                else:
                    result = {"code": "001", "message": "参数完整性验证不通过",
                              "data": {"expect_result": expect_result, "actual_result": actual_result, "params": params}}
                    logger.error(f"[{self.__class__.__name__}] Parameters Complete Validation Failure")
            else:
                raise TypeError()
        except TypeError as e:
            logger.error(f"[{self.__class__.__name__}] Type Error.")
            logger.error(f"[{self.__class__.__name__}] Error Info: self.response_content variable type isn't dict")
            result = {"code": "400", "message": "数据类型错误", "data": {"expect_result": "", "actual_result": "", "params": None}}
        except Exception as e:
            logger.error(f"[{self.__class__.__name__}.{sys._getframe().f_code.co_name}] {e}")
            result = {"code": "500", "message": "参数完整性验证出错", "data": {"expect_result": "", "actual_result": "", "params": None}}
        finally:
            self.params.clear()
        return result

    def compare_param_value(self, expect_param_value: str, json_path: str):
        """
        特殊参数值验证
        :param expect_param_value: 期望结果
        :param json_path: 从后端返回数据提取待验证的参数值，作为实际结果
        :return: 以字典形式返回，code表示方法执行结果，message表示方法执行结果描述，data表示方法需返回的数据
        """
        try:
            expect_param_value = str(expect_param_value) if not isinstance(expect_param_value,
                                                                           str) else expect_param_value
            try:
                logger.debug(expect_param_value)
                expect_param_value = eval(expect_param_value)
            except (NameError, SyntaxError) as e:
                logger.info(f"'expect_param_value' value can't be run as an expression: {expect_param_value}")

            response_content = json.loads(self.response_content, strict=False)
            return_result = jsonpath.jsonpath(response_content, json_path)
            actual_param_value = return_result if return_result else None
            actual_param_value = actual_param_value[0] if actual_param_value and len(actual_param_value) == 1 else actual_param_value
            logger.info(
                f"actual_param_value: {actual_param_value}:{type(actual_param_value)}, expect_param_value: {expect_param_value}:{type(expect_param_value)}")

            if expect_param_value == actual_param_value:
                result = {"code": "000", "message": "特殊参数值验证通过",
                          "data": {"expect_result": expect_param_value, "actual_result": pymysql.escape_string(str(actual_param_value))}}
            else:
                result = {"code": "900", "message": "特殊参数值验证不通过",
                          "data": {"expect_result": expect_param_value, "actual_result": pymysql.escape_string(str(actual_param_value))}}
        except Exception as e:
            logger.error(f"[{self.__class__.__name__}.{sys._getframe().f_code.co_name}] {e}")
            result = {"code": "900", "message": "特殊参数值验证出错", "data": {"expect_result": "", "actual_result": ""}}
        return result
